package ace.cmp.security.oauth2.resource.server.autoconfig.config;

import ace.cmp.json.api.JsonService;
import ace.cmp.security.autoconfig.SecurityBootstrapAutoConfigure;
import ace.cmp.security.core.impl.constant.Oauth2SecurityConstant;
import ace.cmp.security.oauth2.resource.server.core.properties.Oauth2ResourceServerSecurityProperties;
import ace.cmp.security.oauth2.resource.server.core.web.BearerTokenDynamicResolver;
import ace.cmp.security.oauth2.resource.server.core.web.ResourceServerAuthenticationEntryPoint;
import ace.cmp.security.oauth2.resource.server.core.web.access.ResourceServerAccessDeniedHandler;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * @author caspar
 * @date 2023/2/6 20:09 自动配置oauth2 resource server
 */
@Slf4j
@EnableConfigurationProperties({
  OAuth2ResourceServerProperties.class,
  Oauth2ResourceServerSecurityProperties.class
})
@AutoConfigureAfter({OAuth2ClientAutoConfiguration.class, SecurityBootstrapAutoConfigure.class})
@AutoConfigureBefore({OAuth2ResourceServerAutoConfiguration.class})
@AllArgsConstructor
@Configuration
public class Oauth2ResourceServerSecurityAutoConfigure {

  @Bean
  @Order(Oauth2SecurityConstant.OAUTH2_RESOURCE_SERVER_AUTOCONFIGURE_ORDER)
  public SecurityFilterChain defaultSecurityFilterChain(
      HttpSecurity httpSecurity,
      ApplicationContext applicationContext,
      OpaqueTokenIntrospector opaqueTokenIntrospector,
      Oauth2ResourceServerSecurityProperties securityProperties,
      JsonService jsonService,
      @Qualifier("requestMappingHandlerMapping")
          RequestMappingHandlerMapping requestMappingHandlerMapping)
      throws Exception {

    List<String> preAuthorizeUrls = new ArrayList<>(securityProperties.getSecurityAntPaths());

    OAuth2ResourceServerConfigurer resourceServerConfigurer =
        new OAuth2ResourceServerConfigurer(applicationContext);

    resourceServerConfigurer.opaqueToken().introspector(opaqueTokenIntrospector);
    resourceServerConfigurer.bearerTokenResolver(
        new BearerTokenDynamicResolver(
            new DefaultBearerTokenResolver(), preAuthorizeUrls, requestMappingHandlerMapping));
    resourceServerConfigurer.authenticationEntryPoint(
        new ResourceServerAuthenticationEntryPoint(jsonService));
    resourceServerConfigurer.accessDeniedHandler(
        new ResourceServerAccessDeniedHandler(jsonService));

    httpSecurity
        .headers(
            httpSecurityHeadersConfigurer ->
                httpSecurityHeadersConfigurer.frameOptions(
                    frameOptionsConfig -> frameOptionsConfig.sameOrigin()))
        .authorizeHttpRequests(registry -> registry.anyRequest().permitAll())
        .csrf(csrfConfigurer -> csrfConfigurer.disable())
        .apply(resourceServerConfigurer);

    return httpSecurity.build();
  }
}
